package com.hoppinzq.video.service;

/**
 * @author:ZhangQi
 **/
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.hoppinzq.bean.VideoComment;
import com.hoppinzq.bean.VideoEntity;
import com.hoppinzq.bean.VideoExtra;
import com.hoppinzq.service.aop.annotation.ApiCache;
import com.hoppinzq.service.aop.annotation.ApiMapping;
import com.hoppinzq.service.aop.annotation.ApiServiceMapping;
import com.hoppinzq.service.bean.*;
import com.hoppinzq.service.cache.apiCache;
import com.hoppinzq.service.util.DateFormatUtil;
import com.hoppinzq.service.util.RedisUtils;
import com.hoppinzq.service.util.SpringUtils;
import com.hoppinzq.service.util.StringUtil;
import com.hoppinzq.video.dao.VideoDao;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Async;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

@ApiServiceMapping(title = "视频服务", description = "视频服务",roleType = ApiServiceMapping.RoleType.RIGHT)
public class VideoService {

    @Autowired
    private VideoDao videoDao;
    @Autowired
    private RedisUtils redisUtils;

    @ApiMapping(value="v2o")
    public void video2Oss(){

    }

    @ApiMapping(value="getZbs",title = "获取直播状态", description = "获取直播状态")
    public Map getZhiboStateByUser(Integer user_id){
        return videoDao.getZhiboStateByUser(user_id);
    }

    @ApiMapping(value="getZb",title = "获取直播信息", description = "获取直播信息")
    public Map getZhiboByUser(Integer user_id){
        return videoDao.getZhiboByUser(user_id);
    }


    @ApiMapping(value = "getTCode", title = "获取推流码", description = "获取推流码",type = ApiMapping.Type.POST)
    public Map getTCode(String streamName){
        Map map=new HashMap();
        map.put("rtmp","rtmp://179976.push.tlivecloud.com/live/");
        Long l=System.currentTimeMillis();
        String ls=l.toString();
        ls=ls.substring(0,ls.length()-3);
        Long ll=Long.valueOf(ls);
        ll+=60*60*24;
        String su=getSafeUrl("701d1af02b095ca7021b91b0afcfe017", streamName, ll);
        map.put("time",ll);
        map.put("code",streamName+"?"+su);
        return map;
    }

    @ApiMapping(value = "getBCode", title = "获取播放地址", description = "获取播放地址",type = ApiMapping.Type.POST)
    public Map getBCode(String streamName){
        Map map=new HashMap();
        map.put("rtmp","rtmp://live.hoppinzq.com/live/"+streamName);
        map.put("flv","http://live.hoppinzq.com/live/"+streamName+".flv");
        map.put("m3u8","http://live.hoppinzq.com/live/"+streamName+".m3u8");
        Long l=System.currentTimeMillis();
        String ls=l.toString();
        ls=ls.substring(0,ls.length()-3);
        Long ll=Long.valueOf(ls);
        ll+=60*60*24;
        String su=getSafeUrl("701d1af02b095ca7021b91b0afcfe017", streamName, ll);
        map.put("time",ll);
        map.put("excode","?"+su);
        return map;
    }

    @ApiMapping(value = "getRC", title = "推荐i个", description = "推荐5个")
    public List<Map> getAllVideo2Redis(int i){
        JSONArray videoArray=new JSONArray();
        Object redisLists=redisUtils.get("VIDEO:ALLVIDEO");
        if(redisLists==null){
            List<Map> videosList=videoDao.getAllVideo();
            videoArray=JSONArray.parseArray(JSON.toJSONString(videosList));
            redisUtils.set("VIDEO:ALLVIDEO",videoArray);
        }else{
            videoArray=(JSONArray)redisLists;
        }
        String js=JSONObject.toJSONString(videoArray, SerializerFeature.WriteClassName);
        List<Map> list = JSONObject.parseArray(js,Map.class);
        Collections.shuffle(list);
        return list.subList(0,i);
    }
    /**
     * 测试接口
     * @return
     */
    @ApiMapping(value = "test", title = "测试接口", description = "测试接口")
    public String test(String str){
        List<ServiceApiBean> serviceApiBeans= apiCache.outApiList;
        return "测试接口:"+ JSONObject.toJSON(serviceApiBeans);
    }

    @ApiMapping(value = "getVideo", title = "获取视频列表", description = "视频列表根据查询条件查询")
    public List<Map> getVideo(VideoEntity entity) throws ParseException, IOException {
        if(entity.getVideo_createTime_extra()!=null){
            Integer createTime_extra=entity.getVideo_createTime_extra();
            Calendar now=Calendar.getInstance();
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date nowData=now.getTime();
            String date=sdf.format(nowData);
            if(createTime_extra==1){
                date=date.replaceAll(date.split(" ")[1],"00:00:00");
            }else if(createTime_extra==2){
                date= DateFormatUtil.getFirstAndLastOfWeek(date);
                date=date.replaceAll(date.split(" ")[1],"00:00:00");
            }else if(createTime_extra==3){
                date=DateFormatUtil.getFirstDayDateOfMonth(nowData);
                date=date.replaceAll(date.split(" ")[1],"00:00:00");
            }else if(createTime_extra==4){
                String _year=date.split("-")[0];
                date=_year+"-01-01 00:00:00";
            }
            entity.setVideo_createTime(date);
        }
        if(entity.getVideo_name()!=null&&!"".equals(entity.getVideo_name())){
            int pageIndex = entity.getVideo_page_num();
            int pageSize=entity.getVideo_page_size();
            Integer start =0;
            Integer end = 0;
            if(pageIndex!=0){
                start = pageIndex;
                end = pageIndex + pageSize;
            }
            Analyzer analyzer = new IKAnalyzer();
            BooleanQuery.Builder query = new BooleanQuery.Builder();
            //用户搜索历史
//            JSONObject user= (JSONObject) LoginUser.getUserHold();
//            blogService.insertSearchKey(blogVo.getSearch(),user);
            String[] fields = {"video_name","user_name", "video_miaoshu", "video_fenlei_name"};
            //设置影响排序的权重, 这里设置域的权重
            Map<String, Float> boots = new HashMap<>();
            boots.put("video_name", 1000000f);
            boots.put("user_name", 100000f);
            boots.put("video_miaoshu", 10000f);
            boots.put("video_fenlei_name", 50000f);
            //从多个域查询对象
            //query1 = queryParser.parse("*:*");
            MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(fields, analyzer, boots);
            Query querySearch = multiFieldQueryParser.parse(entity.getVideo_name());
            query.add(querySearch, BooleanClause.Occur.MUST);
            String os = System.getProperty("os.name").toLowerCase();
            String indexPath="";
            if(os.contains("windows")) {
                indexPath="D:/index/video";
            }else{
                indexPath="/home/index/video";
            }
            Directory dir = FSDirectory.open(Paths.get(indexPath));
            IndexReader indexReader = DirectoryReader.open(dir);
            IndexSearcher indexSearcher = new IndexSearcher(indexReader);
            TopDocs topDocs = indexSearcher.search(query.build(),end);

            ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            List<Map> videos=new ArrayList<>();
            if (scoreDocs != null) {
                for (int i = start; i < end; i ++) {
                    if(start>topDocs.totalHits||topDocs.totalHits==i){
                        break;
                    }
                    //获取查询到的文档唯一标识, 文档id, 这个id是lucene在创建文档的时候自动分配的
                    int docID = scoreDocs[i].doc;
                    Document doc = indexReader.document(docID);
                    Map video=new HashMap();
                    video.put("video_id",doc.get("video_id"));
                    video.put("video_name",doc.get("video_name"));
                    video.put("video_miaoshu",doc.get("video_miaoshu"));
                    video.put("user_name",doc.get("user_name"));
                    video.put("user_image",doc.get("user_image"));
                    video.put("video_dianzan",doc.get("video_dianzan"));
                    video.put("video_bofang",doc.get("video_bofang"));
                    video.put("video_xihuan",doc.get("video_xihuan"));
                    video.put("video_fenlei_name",doc.get("video_fenlei_name"));
                    video.put("video_createTime",doc.get("video_createTime"));
                    video.put("image_path",doc.get("image_path"));
                    videos.add(video);
                }
            }
            indexReader.close();
            return videos;
        }else{
            return videoDao.getVideo(entity);
        }
    }

    @ApiMapping(value = "countVideo", title = "获取视频数量", description = "获取视频数量")
    public Integer countVideo(VideoEntity entity){
        if(entity.getVideo_createTime_extra()!=null){
            Integer createTime_extra=entity.getVideo_createTime_extra();
            Calendar now=Calendar.getInstance();
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date nowData=now.getTime();
            String date=sdf.format(nowData);
            if(createTime_extra==1){
                date=date.replaceAll(date.split(" ")[1],"00:00:00");
            }else if(createTime_extra==2){
                date= DateFormatUtil.getFirstAndLastOfWeek(date);
                date=date.replaceAll(date.split(" ")[1],"00:00:00");
            }else if(createTime_extra==3){
                date=DateFormatUtil.getFirstDayDateOfMonth(nowData);
                date=date.replaceAll(date.split(" ")[1],"00:00:00");
            }else if(createTime_extra==4){
                String _year=date.split("-")[0];
                date=_year+"-01-01 00:00:00";
            }
            entity.setVideo_createTime(date);
        }
        return videoDao.countVideo(entity);
    }

    @ApiMapping(value = "getVideoDetail", title = "获取视频详情", description = "通过视频ID获取视频详情")
    public Map getVideoDetail(Integer video_id){
        return videoDao.getVideoDetail(video_id);
    }

    @ApiMapping(value = "getVideoSimilar", title = "获取相似视频", description = "通过视频名获取相似视频")
    public List getVideoSimilar(String video_name){
        List<Map> video_similar1=videoDao.queryVideoBytypeAndNumber(1,7,null,video_name.substring(0,video_name.length()-1));
        if(video_similar1.size()<7){
            List<Map> video_similar2=videoDao.queryVideoBytypeAndNumber(1,7,null,video_name.substring(0,1));
            List<Map> video_similar3=diff(video_similar2,video_similar1);
            video_similar1.addAll(video_similar3);
            if(video_similar1.size()<7){
                List<Map> video_similar4=videoDao.queryVideoBytypeAndNumber(1,7,null,"");
                List<Map> video_similar5=diff(video_similar4,video_similar1);
                video_similar1.addAll(video_similar5);
            }
        }
        return video_similar1;
    }

    @ApiMapping(value = "queryComments", title = "获取评论", description = "通过视频ID获取评论")
    public List<Map> queryComments(Integer video_id){
        Map params=new HashMap();
        params.put("video_id",video_id);
        return videoDao.queryComments(params);
    }

    /**
     * 将所有博客存入索引库
     * 先清空索引库再新增，相当于把数据库内所有博客重新刷入索引库
     * 管理员权限可操作（暂时注释掉了）
     */
    @ApiMapping(value = "createVideoIndex", title = "重新生成视频索引库",description = "需要管理员权限，先清空索引库数据在新增")
    public void createBlogIndex(){
        String os = System.getProperty("os.name").toLowerCase();
        String indexPath="";
        if(os.contains("windows")) {
            indexPath="D:/index/video";
        }else{
            indexPath="/home/index/video";
        }
        List<Map> videosList=videoDao.getAllVideo();
        try {
            List<Document> docList = new ArrayList<>();
            for (Map video : videosList) {
                Document document = new Document();
                //创建域对象并且放入文档对象中
                //给标题，描述，喜欢数，收藏数，内容创建索引
                //使用Int/Long/DoublePoint来表示数值型字段的,默认不存储,不排序,也不支持加权
                /**
                 * 是否分词: 是否索引: 是否存储:
                 */
                document.add(new StringField("video_id", String.valueOf(video.get("video_id")), Field.Store.YES));
                document.add(new TextField("video_name", String.valueOf(video.get("video_name")), Field.Store.YES));
                document.add(new TextField("video_miaoshu", String.valueOf(video.get("video_miaoshu")), Field.Store.NO));
                document.add(new TextField("user_name", String.valueOf(video.get("user_name")), Field.Store.YES));
                document.add(new TextField("user_image", String.valueOf(video.get("user_image")), Field.Store.YES));
                document.add(new IntPoint("video_dianzan", Integer.valueOf(String.valueOf(video.get("video_dianzan")))));
                document.add(new StoredField("video_dianzan", Integer.valueOf(String.valueOf(video.get("video_dianzan")))));
                document.add(new NumericDocValuesField("video_dianzan", Integer.valueOf(String.valueOf(video.get("video_dianzan")))));

                document.add(new IntPoint("video_bofang", Integer.valueOf(String.valueOf(video.get("video_bofang")))));
                document.add(new StoredField("video_bofang", Integer.valueOf(String.valueOf(video.get("video_bofang")))));
                document.add(new NumericDocValuesField("video_bofang", Integer.valueOf(String.valueOf(video.get("video_bofang")))));

                document.add(new IntPoint("video_xihuan", Integer.valueOf(String.valueOf(video.get("video_xihuan")))));
                document.add(new StoredField("video_xihuan", Integer.valueOf(String.valueOf(video.get("video_xihuan")))));
                document.add(new NumericDocValuesField("video_xihuan", Integer.valueOf(String.valueOf(video.get("video_xihuan")))));
                document.add(new StringField("video_fenlei_name", String.valueOf(video.get("video_fenlei_name")), Field.Store.YES));
                document.add(new StringField("image_path", String.valueOf(video.get("image_path")), Field.Store.YES));
                //用于对时间排序
                //document.add(new TextField("updateDate", DateUtil.formatDate(blog.getUpdateTime()), Field.Store.YES));
                //添加排序支持
                //document.add(new SortedDocValuesField("updateDate", new BytesRef(DateUtil.formatDate(blog.getUpdateTime()))));
                //大小,数字类型使用point添加到索引中,同时如果需要存储,由于没有Store,所以需要再创建一个StoredField进行存储
                document.add(new StringField("video_createTime",  String.valueOf(video.get("video_createTime")), Field.Store.YES));
                docList.add(document);
            }
            //创建分词器, IK分词器,
            Analyzer analyzer = new IKAnalyzer();
            Directory dir = FSDirectory.open(Paths.get(indexPath));
            IndexWriterConfig config = new IndexWriterConfig(analyzer);
            IndexWriter indexWriter = new IndexWriter(dir, config);
            indexWriter.deleteAll();//先清空索引库
            for (Document doc : docList) {
                indexWriter.addDocument(doc);
            }
            indexWriter.close();
        }catch (Exception ex){
            throw new RuntimeException("将所有video存入索引库:"+ex);
        }
    }

    /**
     * 返回集合c2不包含集合c1的元素
     * @param c1 之后的视频集合
     * @param c2 开始的视频集合
     * @return
     */
    private static List<Map> diff(List<Map> c1, List<Map> c2) {
        if (c1 != null && c1.size() != 0 && c2 != null && c2.size() != 0) {
            List<Map> difference = new ArrayList();
            Iterator i$ = c1.iterator();
            while(i$.hasNext()) {
                Map item = (Map)i$.next();
                if (!c2.contains(item)) {
                    difference.add(item);
                }
            }
            return difference;
        } else {
            return c1;
        }
    }


    public void insertVideo(VideoEntity entity){
        videoDao.insertVideo(entity);
    }

    public List<Map> getVideoFenlei(){
        return videoDao.getVideoFenlei();
    }


    public void updateVideo(VideoEntity entity){
        videoDao.updateVideo(entity);
    }

    public void deleteVideo(Integer video_id){
        videoDao.deleteVideo(video_id);
        videoDao.deleteVideoCommentByVideoId(video_id);
        videoDao.deleteVideoExtraByVideoId(video_id);
    }

    public void deleteVideoComment(Integer comment_id){
        videoDao.deleteVideoComment(comment_id);
    }
    /**
     * 异步记录日志
     * @param requestInfo
     */
    @Async
    public void insertLog(RequestInfo requestInfo){
        videoDao.insertLog(requestInfo);
    }

    /**
     *
     * @param type 为1表示按照播放排序，为2表示按照喜欢排序
     * @param number 查几条
     * @param video_fenlei_id 一个查询条件是分类id
     * @param video_name 一个查询条件是视频名称模糊查询
     * @return
     */
    public List<Map> queryVideoBytypeAndNumber(Integer type,Integer number,Integer video_fenlei_id,String video_name){
        return videoDao.queryVideoBytypeAndNumber(type, number, video_fenlei_id, video_name);
    }

    public Map getVideoTuijian(){
        return videoDao.getVideoTuijian();
    }


    public Integer insertComment(VideoComment videoComment){
       return videoDao.insertComment(videoComment);
    }

    public void insertVideoExtra(VideoExtra videoExtra){
        videoDao.insertVideoExtra(videoExtra);
    }

    private static String getSafeUrl(String key, String streamName, long txTime) {
        String input = new StringBuilder().
                append(key).
                append(streamName).
                append(Long.toHexString(txTime).toUpperCase()).toString();

        String txSecret = null;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            txSecret  = byteArrayToHexString(
                    messageDigest.digest(input.getBytes("UTF-8")));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return txSecret == null ? "" :
                new StringBuilder().
                        append("txSecret=").
                        append(txSecret).
                        append("&").
                        append("txTime=").
                        append(Long.toHexString(txTime).toUpperCase()).
                        toString();
    }
    private static final char[] DIGITS_LOWER =
            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    private static String byteArrayToHexString(byte[] data) {
        char[] out = new char[data.length << 1];

        for (int i = 0, j = 0; i < data.length; i++) {
            out[j++] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4];
            out[j++] = DIGITS_LOWER[0x0F & data[i]];
        }
        return new String(out);
    }
}
